#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <math.h>
#include <GL/glut.h>

#include "tc.h"

#define BOID_WIDTH    512
#define BOID_HEIGHT   512
#define MAPW          2048
#define MAPH          2048
#define BOID_MAX_X    (BOID_WIDTH / 2)
#define BOID_MAX_Y    (BOID_HEIGHT / 2)
#define BOID_STARTX   100
#define BOID_STARTY   100
#define N_MAPS        2 //5
/* static GLfloat viewerDistance = 3.0; */

char mapData[N_MAPS][80000000];

/* char map1Data[80000000]; */
/* char map2Data[80000000]; */

static GLfloat scale = 1.0;
static GLfloat viewStartx = -(MAPW / 16);
static GLfloat viewStarty = -(MAPH / 16);
static GLfloat viewWidth  = MAPW / 2;
static GLfloat viewHeight = MAPH / 2;

static GLint texNum = 0;
static GLint mapID  = 0;

char mapList[N_MAPS][64] = { "map-00.raw",
		             "map-01.raw",
			     /* "map-extra-neon.raw", */
			     /* "map-extra-edge.raw", */
			     /* "map-extra-gaussian.raw" */
			   };

GLuint mapBits[2048][2048];


enum GlutWheel {
	WHEEL_UP = 3,
	WHEEL_DOWN
};

void init(void)
{
	/* initBoids(); */
}

void idleUpdate(void)
{
	makeBoidsUpdate();
}

void setupBack()
{

}

void drawBk(void){

}

float lpos[] = {1.0, 0.5, 1.0, 0.1};
static unsigned int mapBitsSet = 0;

void loadMapBits(void)
{
	if (mapBitsSet) return;
	mapBitsSet = 1;

	int i, j;
	GLfloat c[4];

	for (i = 0; i < MAPH; i++) {
		for(j = 0; j < MAPW; j++) {
			glReadPixels(i, j, 1, 1, GL_RGBA, GL_FLOAT, c);
			if (c[0] < 0.4 && c[1] < 0.4 && c[2] < 0.4) 
				mapBits[i][j] = 1;
			else
				mapBits[i][j] = 0;
		}
	}

	mapID++;
}

void display(void)
{
	glViewport(viewStartx, viewStarty, viewWidth, viewHeight);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glLoadIdentity();

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, mapID);

	glColor3f(1.0, 1.0, 1.0);

	glBegin(GL_QUADS);
	glTexCoord2f(0.0f, 1.0f);
	glVertex3f(-scale, -scale, 0.0f);

	glTexCoord2f(0.0f, 0.0f);
	glVertex3f(-scale, scale, 0.0f);

	glTexCoord2f(1.0f, 0.0f);
	glVertex3f(scale, scale, 0.0f);

	glTexCoord2f(1.0f, 1.0f);
	glVertex3f(scale, -scale, 0.0f);
	glEnd();
 
	loadMapBits();

	glClear(GL_DEPTH_BUFFER_BIT);

	drawAllBoids();

	glutSwapBuffers();

	glutPostRedisplay();

	initBoids();
}

static inline void zoomIn(void)
{
	scale += 0.1;
	viewStartx -= (BOID_WIDTH / 11);
	viewStarty -= (BOID_HEIGHT / 11);
	viewWidth  *= 1.1;
	viewHeight *= 1.1;
}

static inline void zoomOut(void)
{
	if (viewWidth > BOID_WIDTH 
	    && viewHeight > BOID_HEIGHT) {
		scale -= 0.1;
		viewStartx += (BOID_WIDTH / 10);
		viewStarty += (BOID_HEIGHT / 10);
		viewWidth /= 1.1;
		viewHeight /= 1.1;
	}
}

int pMouseX, pMouseY;
GLfloat c[4];

void mouseHandler(int button, int state, int x, int y)
{
	/* printf("%d, %d\n", x, y); */

	if(state == GLUT_UP) {
		/* glGetTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, c); */
		/* glGetTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, c); */

		GLint viewport[4];
		GLdouble modelview[16];
		GLdouble projection[16];
		GLfloat winX, winY, winZ;
		GLdouble posX, posY, posZ;

		glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
		glGetDoublev(GL_PROJECTION_MATRIX, projection);
		glGetIntegerv(GL_VIEWPORT, viewport);

		winX = (float)x;
		winY = (float)y;
	
		glReadPixels(x, y, 1, 1, GL_RGBA, GL_FLOAT, c);
		gluUnProject(winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);

		printf("OGL Position: %f, %f, %f  / %f, %f, %f, %f\n", posX, posY, posZ, c[0], c[1], c[2], c[3]);


		if (button == WHEEL_UP)
			zoomIn();
		else if (button == WHEEL_DOWN)
			zoomOut();
	}
}

void passiveMouseMotion(int x, int y)
{
	pMouseY = y;
	pMouseX = x;
}

void mouseMotion(int x, int y)
{
	viewStartx += (x - pMouseX);
	viewStarty += -(y - pMouseY);

	pMouseY = y;
	pMouseX = x;
}

int setupTexture(char *buffer) 
{
	glBindTexture(GL_TEXTURE_2D, texNum);

	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, MAPW, MAPH, 0, GL_RGB, 
		     GL_UNSIGNED_BYTE, (const GLvoid *) buffer);


	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	texNum++;

	return (texNum - 1);
}

void doTexStuff(const char *path,
		const char *fName,
		int width, int height,
		char *buffer){
	char fPath[256];
	FILE *im;

	strcpy(fPath, path);
	strcat(fPath, fName);
	im = fopen(fPath, "r");

	if(im == NULL) {
		perror(path);
		exit(1);
	}

	/* buffer = malloc(sizeof(char) * height * width * 3); */
	fread(buffer, sizeof(char), height * width * 3, im);

	fclose(im);
}

void keyboard(unsigned char key, int x, int y)
{
	switch(key) {
	case 27:
		exit(0);
		break;
	case 'm':
		mapID = (++mapID) % N_MAPS;
		break;

	case 'y':
		if(scale > 1.0) 
			zoomOut();
		break;

	case 'z':
		if(scale < 10) 
			zoomIn();
		break;

	default:
		break;
	}
}

#define R 1
#define G 2
#define B 3
#define W 4

void processMenuEvents(int option)
{

}

void createGlutMenus(void)
{
	int menu;

	menu = glutCreateMenu(processMenuEvents);

	glutAddMenuEntry("Red", R);
	glutAddMenuEntry("Green", G);
	glutAddMenuEntry("Blue", B);

	glutAttachMenu(GLUT_RIGHT_BUTTON);
}

int main(int argc, char **argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA);
	glutInitWindowSize(BOID_WIDTH, BOID_HEIGHT);
	glutInitWindowPosition(BOID_STARTX, BOID_STARTY);
	glutCreateWindow("Taste of Chicago");

	init();

	glutKeyboardFunc(keyboard);
	glutMouseFunc(mouseHandler);
	glutPassiveMotionFunc(passiveMouseMotion);
	glutMotionFunc(mouseMotion);
	glutDisplayFunc(display);
	glutIdleFunc(idleUpdate);
	/* setupBack(); */

	/* gluInit(); */
	glEnable(GL_DEPTH_TEST);
	glClearColor(0.0, 0.0, 0.0, 1.0);

	glBindTexture(GL_TEXTURE_2D, 0);
	/* doTexStuff((char *) "./images/", (char *) mapList[mapID],  */
	/* 	   MAPW, MAPH, map1Data); */
	/* setupTexture(map1Data); */

	/* doTexStuff((char *) "./images/", (char *) mapList[(++mapID % 2)],  */
	/* 	   MAPW, MAPH, map2Data); */
	/* setupTexture(map2Data); */

	int i;

	for(i = 0; i < N_MAPS; i++) {
		doTexStuff((char *) "./images/", (char *) mapList[i], 
			   MAPW, MAPH, mapData[i]);
		setupTexture(mapData[i]);
	}

	/* createGlutMenus(); */
	/* system("mplayer files/mm.mp3"); */
	glutMainLoop();

	return 0;
}
